﻿using UnityEngine;
using UnityEditor;
using UnityEngine.Rendering;
using static DeferredShadingSettings;

public partial class DeferredShading {
	DeferredShadingSettings settings;
	ScriptableRenderContext context;
	Camera camera;

	const string bufferName = "Deferred Shading";
	CommandBuffer buffer = new CommandBuffer {
		name = bufferName
	};

	enum Pass {
		DeferLighting,
		DisplayGBuffer0,
		DisplayGBuffer1,
		DisplayGBuffer2,
		DisplayGBuffer3
	}

	//RenderBuffer[] gBuffer = new RenderBuffer[4];
	//RenderTexture[] gBufferTexture = new RenderTexture[4];
	const int gBufferSize = 5;
	int[] gBuffer = new int[gBufferSize] {
		Shader.PropertyToID("_GBuffer0_Output"),
		Shader.PropertyToID("_GBuffer1_Output"),
		Shader.PropertyToID("_GBuffer2_Output"),
		Shader.PropertyToID("_GBuffer3_Output"),
		Shader.PropertyToID("_GBuffer4_Output")};

	int[] gBufferId = new int[gBufferSize] {   
		Shader.PropertyToID("_GBuffer0"),
		Shader.PropertyToID("_GBuffer1"),
		Shader.PropertyToID("_GBuffer2"),
		Shader.PropertyToID("_GBuffer3"),
		Shader.PropertyToID("_GBuffer4")};
	int gBufferDepth;
	int gBufferDepthId = Shader.PropertyToID("_GBufferDepth");
	public bool IsActive => settings != null && settings.useDeferredShading;

	int viewMartixId = Shader.PropertyToID("MatrixV");
	int projectionMartixId = Shader.PropertyToID("MatrixP");
	int invProjectionMartixId = Shader.PropertyToID("invMatrixVP");
	//int cameraPositionId = Shader.PropertyToID("_CameraPosition");

	public DeferredShading() {
	}

	public void Setup(ScriptableRenderContext context, Camera camera, DeferredShadingSettings deferredShadingSettings) {
		this.context = context;
		this.camera = camera;
		this.settings = //deferredShadingSettings;
			camera.cameraType <= CameraType.SceneView ? deferredShadingSettings : null;

		if (deferredShadingSettings.useDeferredShading) {
			for (int i = 0; i < gBufferSize; i++)
				buffer.GetTemporaryRT(gBuffer[i], camera.pixelWidth, camera.pixelHeight, 0, FilterMode.Point,RenderTextureFormat.ARGBHalf);//这里开到16bit是为了能够和hdr形成效果
			buffer.GetTemporaryRT(gBufferDepth, camera.pixelWidth, camera.pixelHeight, 24, FilterMode.Point, RenderTextureFormat.Depth);
			context.ExecuteCommandBuffer(buffer);
			buffer.Clear();
		}
		ApplySceneViewState();
	}
	public void Clean() {
		if (IsActive) {
			for (int i = 0; i < gBufferSize; i++)
				buffer.ReleaseTemporaryRT(gBuffer[i]);
			context.ExecuteCommandBuffer(buffer);
			buffer.Clear();
		}
	}
	private void globalAttrSetup() {
		for (int i = 0; i < gBufferSize; i++)
			buffer.SetGlobalTexture(gBufferId[i], gBuffer[i]);
		buffer.SetGlobalTexture(gBufferDepthId, gBufferDepth);
		buffer.SetGlobalMatrix(viewMartixId, camera.worldToCameraMatrix);
		buffer.SetGlobalMatrix(projectionMartixId, camera.projectionMatrix);
		buffer.SetGlobalMatrix(invProjectionMartixId, /*camera.cameraToWorldMatrix*/
			Matrix4x4.Inverse(camera.worldToCameraMatrix) * Matrix4x4.Inverse(camera.projectionMatrix));
	}

	public void Render() {
		globalAttrSetup();

		buffer.DrawProcedural(
			Matrix4x4.identity, settings.Material,
			(int)Pass.DeferLighting, MeshTopology.Triangles, 3);
		context.ExecuteCommandBuffer(buffer);
		buffer.Clear();
	}
	public bool isDisplayGBuffer() {
		return IsActive && settings.displayGBuffer;
	}
	public void DisplayGBuffer() {
		float smallRectH = (int)(camera.pixelHeight / 5);
		float smallRectW = (int)(camera.pixelWidth / 5);

		globalAttrSetup();

		buffer.SetRenderTarget(
			BuiltinRenderTextureType.CameraTarget, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store
		);
		for (int i = 0; i < 5; i++) {
			buffer.SetViewport(new Rect(smallRectW * i, 0, smallRectW, smallRectH));
			buffer.DrawProcedural(
				Matrix4x4.identity, settings.Material,
				(int)Pass.DisplayGBuffer0 + i, MeshTopology.Triangles, 3);
		}
		//buffer.SetViewport(camera.rect);
		context.ExecuteCommandBuffer(buffer);
		buffer.Clear();
	}

	RenderTargetIdentifier[] targetIdentifier = new RenderTargetIdentifier[gBufferSize];
	public void SetRenderTarget() {
		for (int i = 0; i < gBufferSize; i++) targetIdentifier[i] = new RenderTargetIdentifier(gBuffer[i]);
		buffer.SetRenderTarget(targetIdentifier, gBufferDepth);
		buffer.ClearRenderTarget(true, true, Color.black, 1.0f);
		context.ExecuteCommandBuffer(buffer);
		buffer.Clear();
	}
}